package com.qriver.session.controller;

import com.qriver.session.common.CommonResult;
import com.qriver.session.common.SsoConstant;
import com.qriver.session.entity.UserEntity;
import com.qriver.session.redis.RedisOperator;
import com.qriver.session.service.UserService;
import com.qriver.session.utils.CookieUtils;
import com.qriver.session.utils.JsonUtils;
import com.qriver.session.utils.MD5Utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;

import static com.qriver.session.utils.MD5Utils.*;

@Controller
public class SsoController {

    @Autowired
    private UserService userService;

    @Autowired
    private RedisOperator redisOperator;

    /**
     * 跳转到SSO登录界面
     * @return
     */
    @GetMapping("/login")
    public ModelAndView login(HttpServletRequest request, HttpServletResponse response){

        ModelAndView mv = new ModelAndView();
        String redirectUrl = request.getParameter(SsoConstant.REDIRECT_URL);
        mv.addObject(SsoConstant.REDIRECT_URL, redirectUrl);
        // 1. 获取userTicket门票，如果cookie中能够获取到，证明用户登录过，此时签发一个一次性的临时票据并且回跳
        String userTicket = CookieUtils.getCookieValue(request, SsoConstant.COOKIE_USER_TICKET);

        //验证会话
        boolean isVerified = verifyUserTicket(userTicket);
        if(isVerified){//验证通过，直接跳转到redirectUrl路径,同时颁发临时票据
            String tmpTicket = createTmpTicket();
            mv.setViewName("redirect:" + redirectUrl);
            mv.addObject(SsoConstant.REQUEST_TMP_TICKET, tmpTicket);
            return mv;
            //return "redirect:" + redirectUrl + "?" + SsoConstant.REQUEST_TMP_TICKET + "=" + tmpTicket;
        }
        //2. 验证失败（用户没有登录过或登录失效），则进入SSO统一登录界面
        mv.setViewName("login");
        return mv;
    }

    /**
     * 登录接口
     * @return
     */
    @PostMapping("/doLogin")
    public ModelAndView doLogin(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ModelAndView mv = new ModelAndView();

        String redirectUrl = request.getParameter(SsoConstant.REDIRECT_URL);
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        //1、过来非法请求
        if(StringUtils.isEmpty(username) || StringUtils.isEmpty(password)){
            mv.setViewName("login");
            mv.addObject("msg", "用户名或密码不能为空");
            return mv;
        }
        //2、验证用户密码是否正确
        UserEntity user = userService.queryUserForLogin(username, MD5Utils.getMD5Str(password));
        if(user == null){
            mv.setViewName("login");
            mv.addObject("msg", "用户名或密码不能为空");
            return mv;
        }

        //3、登录验证通过后，生成用户会话
        redisOperator.set(SsoConstant.REDIS_USER_TOKEN + ":" + user.getId(), JsonUtils.objectToJson(user));

        //4、生成全局门票，用来表示用户是否在SSO登录过
        String userTicket = UUID.randomUUID().toString().trim();
        //4.1、设置全局门票到cookie中，后续根据该cookie判断用户是否登录过
        CookieUtils.setCookie(request, response, SsoConstant.COOKIE_USER_TICKET, userTicket);

        //5、用户（userId）和全局门票关联，建立全局门票与用户的关系，后续可以根据cookie的门票获取对应用户ID
        redisOperator.set(SsoConstant.REDIS_USER_TICKET + ":" + userTicket, user.getId() + "");

        //6、创建临时门票，用于登录验证。这里的tmpTicket用于颁发给用户进行一次性的验证的票据，有时效性
        String tmpTicket = createTmpTicket();
        mv.setViewName("redirect:" + redirectUrl);
        mv.addObject(SsoConstant.REQUEST_TMP_TICKET, tmpTicket);
        return mv;
    }

    @PostMapping("/verifyTmpTicket")
    @ResponseBody
    public CommonResult verifyTmpTicket(String tmpTicket, HttpServletRequest request, HttpServletResponse response) throws Exception {

        // 使用一次性临时票据来验证用户是否登录，如果登录过，把用户会话信息返回给站点，使用完毕后，需要销毁临时票据
        String tmpTicketValue = redisOperator.get(SsoConstant.REDIS_TMP_TICKET + ":" + tmpTicket);
        if (StringUtils.isEmpty(tmpTicketValue)) {
            return CommonResult.errorUserTicket("用户票据异常");
        }

        // 0. 如果临时票据OK，则需要销毁，并且拿到CAS端cookie中的全局userTicket，以此再获取用户会话
        if (!tmpTicketValue.equals(MD5Utils.getMD5Str(tmpTicket))) {
            return CommonResult.errorUserTicket("用户票据异常");
        } else {
            // 销毁临时票据
            redisOperator.del(SsoConstant.REDIS_TMP_TICKET + ":" + tmpTicket);
        }

        // 1. 验证并且获取用户的userTicket
        String userTicket = CookieUtils.getCookieValue(request, SsoConstant.COOKIE_USER_TICKET);
        if(StringUtils.isEmpty(userTicket)){//处理feign传递的情况
            userTicket = request.getHeader(SsoConstant.COOKIE_USER_TICKET);
        }
        String userId = redisOperator.get(SsoConstant.REDIS_USER_TICKET + ":" + userTicket);
        if (StringUtils.isEmpty(userId)) {
            return CommonResult.errorUserTicket("用户票据异常");
        }

        // 2. 验证门票对应的user会话是否存在
        String userRedis = redisOperator.get(SsoConstant.REDIS_USER_TOKEN + ":" + userId);
        if (StringUtils.isEmpty(userRedis)) {
            return CommonResult.errorUserTicket("用户票据异常");
        }

        // 验证成功，返回OK，携带用户会话
        //return CommonResult.ok(JsonUtils.jsonToPojo(userRedis, UserEntity.class));
        return CommonResult.ok(userRedis);
    }
    /**
     * 登出操作
     * @param request
     * @param response
     * @return
     */
    @PostMapping("/logout")
    @ResponseBody
    public String logout(String userId, HttpServletRequest request, HttpServletResponse response){
        // 获取用户门票
        String userTicket = CookieUtils.getCookieValue(request, SsoConstant.COOKIE_USER_TICKET);
        //删除cookie
        CookieUtils.deleteCookie(request, response, SsoConstant.COOKIE_USER_TICKET);
        //清除redis中的与用户的关联关系的key-val
        redisOperator.del(SsoConstant.REDIS_USER_TICKET + ":" + userTicket);
        //清除redis中的会话
        redisOperator.del(SsoConstant.REDIS_USER_TOKEN + ":" + userId);

        return "成功！";
    }
    /**
     * 创建临时票据
     * @return
     */
    private String createTmpTicket() {
        String tmpTicket = UUID.randomUUID().toString().trim();
        try {
            redisOperator.set(SsoConstant.REDIS_TMP_TICKET + ":" + tmpTicket,
                    getMD5Str(tmpTicket), 600);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return tmpTicket;
    }
    /**
     * 校验CAS全局用户门票
     * @param userTicket
     * @return
     */
    private boolean verifyUserTicket(String userTicket) {

        // 0. 验证CAS门票不能为空
        if (StringUtils.isEmpty(userTicket)) {
            return false;
        }

        // 1. 根据CAS门票查询对应的userId(在Redis缓存中存储)
        String userId = redisOperator.get(SsoConstant.REDIS_USER_TICKET + ":" + userTicket);
        if (StringUtils.isEmpty(userId)) {
            return false;
        }

        // 2. 根据userId查看对应的会话，即判定用户会话是否存在
        String userRedis = redisOperator.get(SsoConstant.REDIS_USER_TOKEN + ":" + userId);
        if (StringUtils.isEmpty(userRedis)) {
            return false;
        }

        return true;
    }


}
